﻿namespace Pathfinding.Serialization
{
    using Pathfinding;
    using Pathfinding.Ionic.Zip;
    using Pathfinding.Serialization.JsonFx;
    using Pathfinding.Util;
    using System;
    using System.IO;
    using System.Runtime.CompilerServices;
    using System.Text;
    using UnityEngine;

    public class AstarSerializer
    {
        private static StringBuilder _stringBuilder = new StringBuilder();
        private const string binaryExt = ".binary";
        private uint checksum;
        private AstarData data;
        private UTF8Encoding encoding;
        private NavGraph[] graphs;
        private const string jsonExt = ".json";
        private GraphMeta meta;
        public JsonReaderSettings readerSettings;
        private SerializeSettings settings;
        private MemoryStream str;
        public JsonWriterSettings writerSettings;
        private ZipFile zip;

        public AstarSerializer(AstarData data)
        {
            this.checksum = uint.MaxValue;
            this.encoding = new UTF8Encoding();
            this.data = data;
            this.settings = SerializeSettings.Settings;
        }

        public AstarSerializer(AstarData data, SerializeSettings settings)
        {
            this.checksum = uint.MaxValue;
            this.encoding = new UTF8Encoding();
            this.data = data;
            this.settings = settings;
        }

        public void AddChecksum(byte[] bytes)
        {
            this.checksum = Checksum.GetChecksum(bytes, this.checksum);
        }

        public void CloseDeserialize()
        {
            this.str.Dispose();
            this.zip.Dispose();
            this.zip = null;
            this.str = null;
        }

        public byte[] CloseSerialize()
        {
            byte[] bytes = this.SerializeMeta();
            this.AddChecksum(bytes);
            this.zip.AddEntry("meta.json", bytes);
            MemoryStream outputStream = new MemoryStream();
            this.zip.Save(outputStream);
            bytes = outputStream.ToArray();
            outputStream.Dispose();
            this.zip.Dispose();
            this.zip = null;
            return bytes;
        }

        public void DeserializeEditorSettings(GraphEditorBase[] graphEditors)
        {
            if (graphEditors != null)
            {
                for (int i = 0; i < graphEditors.Length; i++)
                {
                    if (graphEditors[i] != null)
                    {
                        for (int j = 0; j < this.graphs.Length; j++)
                        {
                            if ((this.graphs[j] != null) && (graphEditors[i].target == this.graphs[j]))
                            {
                                ZipEntry entry = this.zip["graph" + j + "_editor.json"];
                                if (entry != null)
                                {
                                    JsonReader reader = new JsonReader(this.GetString(entry), this.readerSettings);
                                    GraphEditorBase base2 = graphEditors[i];
                                    reader.PopulateObject<GraphEditorBase>(ref base2);
                                    graphEditors[i] = base2;
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }

        public void DeserializeExtraInfo()
        {
            <DeserializeExtraInfo>c__AnonStoreyBA yba = new <DeserializeExtraInfo>c__AnonStoreyBA();
            bool flag = false;
            for (int i = 0; i < this.graphs.Length; i++)
            {
                ZipEntry entry = this.zip["graph" + i + "_extra.binary"];
                if (entry != null)
                {
                    flag = true;
                    MemoryStream stream = new MemoryStream();
                    entry.Extract(stream);
                    stream.Seek(0L, SeekOrigin.Begin);
                    BinaryReader reader = new BinaryReader(stream);
                    GraphSerializationContext ctx = new GraphSerializationContext(reader, null, i);
                    this.graphs[i].DeserializeExtraInfo(ctx);
                }
            }
            if (flag)
            {
                yba.totCount = 0;
                for (int j = 0; j < this.graphs.Length; j++)
                {
                    if (this.graphs[j] != null)
                    {
                        this.graphs[j].GetNodes(new GraphNodeDelegateCancelable(yba.<>m__12));
                    }
                }
                <DeserializeExtraInfo>c__AnonStoreyBB ybb = new <DeserializeExtraInfo>c__AnonStoreyBB();
                ZipEntry entry2 = this.zip["graph_references.binary"];
                if (entry2 == null)
                {
                    throw new Exception("Node references not found in the data. Was this loaded from an older version of the A* Pathfinding Project?");
                }
                MemoryStream stream2 = new MemoryStream();
                entry2.Extract(stream2);
                stream2.Seek(0L, SeekOrigin.Begin);
                ybb.reader = new BinaryReader(stream2);
                int num3 = ybb.reader.ReadInt32();
                ybb.int2Node = new GraphNode[num3 + 1];
                try
                {
                    for (int m = 0; m < this.graphs.Length; m++)
                    {
                        if (this.graphs[m] != null)
                        {
                            this.graphs[m].GetNodes(new GraphNodeDelegateCancelable(ybb.<>m__13));
                        }
                    }
                }
                catch (Exception exception)
                {
                    throw new Exception("Some graph(s) has thrown an exception during GetNodes, or some graph(s) have deserialized more or fewer nodes than were serialized", exception);
                }
                ybb.reader.Close();
                for (int k = 0; k < this.graphs.Length; k++)
                {
                    <DeserializeExtraInfo>c__AnonStoreyBC ybc = new <DeserializeExtraInfo>c__AnonStoreyBC();
                    if (this.graphs[k] != null)
                    {
                        entry2 = this.zip["graph" + k + "_references.binary"];
                        if (entry2 == null)
                        {
                            throw new Exception("Node references for graph " + k + " not found in the data. Was this loaded from an older version of the A* Pathfinding Project?");
                        }
                        stream2 = new MemoryStream();
                        entry2.Extract(stream2);
                        stream2.Seek(0L, SeekOrigin.Begin);
                        ybb.reader = new BinaryReader(stream2);
                        ybc.ctx = new GraphSerializationContext(ybb.reader, ybb.int2Node, k);
                        this.graphs[k].GetNodes(new GraphNodeDelegateCancelable(ybc.<>m__14));
                    }
                }
            }
        }

        public NavGraph[] DeserializeGraphs()
        {
            this.graphs = new NavGraph[this.meta.graphs];
            int index = 0;
            for (int i = 0; i < this.meta.graphs; i++)
            {
                System.Type graphType = this.meta.GetGraphType(i);
                if (!object.Equals(graphType, null))
                {
                    index++;
                    ZipEntry entry = this.zip["graph" + i + ".json"];
                    if (entry == null)
                    {
                        object[] objArray1 = new object[] { "Could not find data for graph ", i, " in zip. Entry 'graph+", i, ".json' does not exist" };
                        throw new FileNotFoundException(string.Concat(objArray1));
                    }
                    NavGraph graph = this.data.CreateGraph(graphType);
                    new JsonReader(this.GetString(entry), this.readerSettings).PopulateObject<NavGraph>(ref graph);
                    this.graphs[i] = graph;
                    if (this.graphs[i].guid.ToString() != this.meta.guids[i])
                    {
                        throw new Exception("Guid in graph file not equal to guid defined in meta file. Have you edited the data manually?\n" + this.graphs[i].guid.ToString() + " != " + this.meta.guids[i]);
                    }
                }
            }
            NavGraph[] graphArray = new NavGraph[index];
            index = 0;
            for (int j = 0; j < this.graphs.Length; j++)
            {
                if (this.graphs[j] != null)
                {
                    graphArray[index] = this.graphs[j];
                    index++;
                }
            }
            this.graphs = graphArray;
            return this.graphs;
        }

        private GraphMeta DeserializeMeta(ZipEntry entry)
        {
            if (entry == null)
            {
                throw new Exception("No metadata found in serialized data.");
            }
            JsonReader reader = new JsonReader(this.GetString(entry), this.readerSettings);
            return (GraphMeta) reader.Deserialize(typeof(GraphMeta));
        }

        private void DeserializeNodeConnections(int index, BinaryReader reader)
        {
        }

        public void DeserializeNodes()
        {
            for (int i = 0; i < this.graphs.Length; i++)
            {
                if ((this.graphs[i] != null) && this.zip.ContainsEntry("graph" + i + "_nodes.binary"))
                {
                }
            }
            for (int j = 0; j < this.graphs.Length; j++)
            {
                if (this.graphs[j] != null)
                {
                    ZipEntry entry = this.zip["graph" + j + "_nodes.binary"];
                    if (entry != null)
                    {
                        MemoryStream stream = new MemoryStream();
                        entry.Extract(stream);
                        stream.Position = 0L;
                        BinaryReader reader = new BinaryReader(stream);
                        this.DeserializeNodes(j, reader);
                    }
                }
            }
            for (int k = 0; k < this.graphs.Length; k++)
            {
                if (this.graphs[k] != null)
                {
                    ZipEntry entry2 = this.zip["graph" + k + "_conns.binary"];
                    if (entry2 != null)
                    {
                        MemoryStream stream2 = new MemoryStream();
                        entry2.Extract(stream2);
                        stream2.Position = 0L;
                        BinaryReader reader2 = new BinaryReader(stream2);
                        this.DeserializeNodeConnections(k, reader2);
                    }
                }
            }
        }

        private void DeserializeNodes(int index, BinaryReader reader)
        {
        }

        public UserConnection[] DeserializeUserConnections()
        {
            ZipEntry entry = this.zip["connections.json"];
            if (entry == null)
            {
                return new UserConnection[0];
            }
            JsonReader reader = new JsonReader(this.GetString(entry), this.readerSettings);
            return (UserConnection[]) reader.Deserialize(typeof(UserConnection[]));
        }

        public uint GetChecksum()
        {
            return this.checksum;
        }

        private string GetString(ZipEntry entry)
        {
            MemoryStream stream = new MemoryStream();
            entry.Extract(stream);
            stream.Position = 0L;
            StreamReader reader = new StreamReader(stream);
            string str = reader.ReadToEnd();
            stream.Position = 0L;
            reader.Dispose();
            return str;
        }

        private static StringBuilder GetStringBuilder()
        {
            _stringBuilder.Length = 0;
            return _stringBuilder;
        }

        public static byte[] LoadFromFile(string path)
        {
            using (FileStream stream = new FileStream(path, FileMode.Open))
            {
                byte[] array = new byte[(int) stream.Length];
                stream.Read(array, 0, (int) stream.Length);
                return array;
            }
        }

        public bool OpenDeserialize(byte[] bytes)
        {
            this.readerSettings = new JsonReaderSettings();
            this.readerSettings.AddTypeConverter(new VectorConverter());
            this.readerSettings.AddTypeConverter(new BoundsConverter());
            this.readerSettings.AddTypeConverter(new LayerMaskConverter());
            this.readerSettings.AddTypeConverter(new MatrixConverter());
            this.readerSettings.AddTypeConverter(new GuidConverter());
            this.readerSettings.AddTypeConverter(new UnityObjectConverter());
            this.str = new MemoryStream();
            this.str.Write(bytes, 0, bytes.Length);
            this.str.Position = 0L;
            try
            {
                this.zip = ZipFile.Read(this.str);
            }
            catch (Exception exception)
            {
                Debug.LogWarning("Caught exception when loading from zip\n" + exception);
                this.str.Dispose();
                return false;
            }
            this.meta = this.DeserializeMeta(this.zip["meta.json"]);
            if (this.meta.version > AstarPath.Version)
            {
                Debug.LogWarning(string.Concat(new object[] { "Trying to load data from a newer version of the A* Pathfinding Project\nCurrent version: ", AstarPath.Version, " Data version: ", this.meta.version }));
            }
            else if (this.meta.version < AstarPath.Version)
            {
                Debug.LogWarning(string.Concat(new object[] { "Trying to load data from an older version of the A* Pathfinding Project\nCurrent version: ", AstarPath.Version, " Data version: ", this.meta.version, "\nThis is usually fine, it just means you have upgraded to a new version.\nHowever node data (not settings) can get corrupted between versions, so it is recommendedto recalculate any caches (those for faster startup) and resave any files. Even if it seems to load fine, it might cause subtle bugs.\n" }));
            }
            return true;
        }

        public void OpenSerialize()
        {
            this.zip = new ZipFile();
            this.zip.AlternateEncoding = Encoding.UTF8;
            this.zip.AlternateEncodingUsage = ZipOption.Always;
            this.writerSettings = new JsonWriterSettings();
            this.writerSettings.AddTypeConverter(new VectorConverter());
            this.writerSettings.AddTypeConverter(new BoundsConverter());
            this.writerSettings.AddTypeConverter(new LayerMaskConverter());
            this.writerSettings.AddTypeConverter(new MatrixConverter());
            this.writerSettings.AddTypeConverter(new GuidConverter());
            this.writerSettings.AddTypeConverter(new UnityObjectConverter());
            this.writerSettings.PrettyPrint = this.settings.prettyPrint;
            this.meta = new GraphMeta();
        }

        public void PostDeserialization()
        {
            for (int i = 0; i < this.graphs.Length; i++)
            {
                if (this.graphs[i] != null)
                {
                    this.graphs[i].PostDeserialization();
                }
            }
        }

        public static void SaveToFile(string path, byte[] data)
        {
            using (FileStream stream = new FileStream(path, FileMode.Create))
            {
                stream.Write(data, 0, data.Length);
            }
        }

        public byte[] Serialize(NavGraph graph)
        {
            StringBuilder stringBuilder = GetStringBuilder();
            new JsonWriter(stringBuilder, this.writerSettings).Write(graph);
            return this.encoding.GetBytes(stringBuilder.ToString());
        }

        public void SerializeEditorSettings(GraphEditorBase[] editors)
        {
            if ((editors != null) && this.settings.editorSettings)
            {
                for (int i = 0; i < editors.Length; i++)
                {
                    if (editors[i] == null)
                    {
                        return;
                    }
                    StringBuilder stringBuilder = GetStringBuilder();
                    new JsonWriter(stringBuilder, this.writerSettings).Write(editors[i]);
                    byte[] bytes = this.encoding.GetBytes(stringBuilder.ToString());
                    if (bytes.Length > 2)
                    {
                        this.AddChecksum(bytes);
                        this.zip.AddEntry("graph" + i + "_editor.json", bytes);
                    }
                }
            }
        }

        public void SerializeExtraInfo()
        {
            <SerializeExtraInfo>c__AnonStoreyB7 yb = new <SerializeExtraInfo>c__AnonStoreyB7();
            if (this.settings.nodes)
            {
                yb.totCount = 0;
                for (int i = 0; i < this.graphs.Length; i++)
                {
                    if (this.graphs[i] != null)
                    {
                        this.graphs[i].GetNodes(new GraphNodeDelegateCancelable(yb.<>m__F));
                    }
                }
                <SerializeExtraInfo>c__AnonStoreyB8 yb2 = new <SerializeExtraInfo>c__AnonStoreyB8();
                MemoryStream output = new MemoryStream();
                yb2.wr = new BinaryWriter(output);
                yb2.wr.Write(yb.totCount);
                yb2.c = 0;
                for (int j = 0; j < this.graphs.Length; j++)
                {
                    if (this.graphs[j] != null)
                    {
                        this.graphs[j].GetNodes(new GraphNodeDelegateCancelable(yb2.<>m__10));
                    }
                }
                if (yb2.c != yb.totCount)
                {
                    throw new Exception("Some graphs are not consistent in their GetNodes calls, sequential calls give different results.");
                }
                byte[] bytes = output.ToArray();
                yb2.wr.Close();
                this.AddChecksum(bytes);
                this.zip.AddEntry("graph_references.binary", bytes);
                for (int k = 0; k < this.graphs.Length; k++)
                {
                    <SerializeExtraInfo>c__AnonStoreyB9 yb3 = new <SerializeExtraInfo>c__AnonStoreyB9();
                    if (this.graphs[k] != null)
                    {
                        MemoryStream stream2 = new MemoryStream();
                        BinaryWriter writer = new BinaryWriter(stream2);
                        yb3.ctx = new GraphSerializationContext(writer);
                        this.graphs[k].SerializeExtraInfo(yb3.ctx);
                        byte[] buffer2 = stream2.ToArray();
                        writer.Close();
                        this.AddChecksum(buffer2);
                        this.zip.AddEntry("graph" + k + "_extra.binary", buffer2);
                        stream2 = new MemoryStream();
                        writer = new BinaryWriter(stream2);
                        yb3.ctx = new GraphSerializationContext(writer);
                        this.graphs[k].GetNodes(new GraphNodeDelegateCancelable(yb3.<>m__11));
                        writer.Close();
                        buffer2 = stream2.ToArray();
                        this.AddChecksum(buffer2);
                        this.zip.AddEntry("graph" + k + "_references.binary", buffer2);
                    }
                }
            }
        }

        public void SerializeGraphs(NavGraph[] _graphs)
        {
            if (this.graphs != null)
            {
                throw new InvalidOperationException("Cannot serialize graphs multiple times.");
            }
            this.graphs = _graphs;
            if (this.zip == null)
            {
                throw new NullReferenceException("You must not call CloseSerialize before a call to this function");
            }
            if (this.graphs == null)
            {
                this.graphs = new NavGraph[0];
            }
            for (int i = 0; i < this.graphs.Length; i++)
            {
                if (this.graphs[i] != null)
                {
                    byte[] bytes = this.Serialize(this.graphs[i]);
                    this.AddChecksum(bytes);
                    this.zip.AddEntry("graph" + i + ".json", bytes);
                }
            }
        }

        private byte[] SerializeMeta()
        {
            this.meta.version = AstarPath.Version;
            this.meta.graphs = this.data.graphs.Length;
            this.meta.guids = new string[this.data.graphs.Length];
            this.meta.typeNames = new string[this.data.graphs.Length];
            this.meta.nodeCounts = new int[this.data.graphs.Length];
            for (int i = 0; i < this.data.graphs.Length; i++)
            {
                if (this.data.graphs[i] != null)
                {
                    this.meta.guids[i] = this.data.graphs[i].guid.ToString();
                    this.meta.typeNames[i] = this.data.graphs[i].GetType().FullName;
                }
            }
            StringBuilder stringBuilder = GetStringBuilder();
            new JsonWriter(stringBuilder, this.writerSettings).Write(this.meta);
            return this.encoding.GetBytes(stringBuilder.ToString());
        }

        private byte[] SerializeNodeConnections(int index)
        {
            return new byte[0];
        }

        public void SerializeNodes()
        {
            if (this.settings.nodes)
            {
                if (this.graphs == null)
                {
                    throw new InvalidOperationException("Cannot serialize nodes with no serialized graphs (call SerializeGraphs first)");
                }
                for (int i = 0; i < this.graphs.Length; i++)
                {
                    byte[] bytes = this.SerializeNodes(i);
                    this.AddChecksum(bytes);
                    this.zip.AddEntry("graph" + i + "_nodes.binary", bytes);
                }
                for (int j = 0; j < this.graphs.Length; j++)
                {
                    byte[] buffer2 = this.SerializeNodeConnections(j);
                    this.AddChecksum(buffer2);
                    this.zip.AddEntry("graph" + j + "_conns.binary", buffer2);
                }
            }
        }

        private byte[] SerializeNodes(int index)
        {
            return new byte[0];
        }

        public void SerializeUserConnections(UserConnection[] conns)
        {
            if (conns == null)
            {
                conns = new UserConnection[0];
            }
            StringBuilder stringBuilder = GetStringBuilder();
            new JsonWriter(stringBuilder, this.writerSettings).Write(conns);
            byte[] bytes = this.encoding.GetBytes(stringBuilder.ToString());
            stringBuilder = null;
            if (bytes.Length > 2)
            {
                this.AddChecksum(bytes);
                this.zip.AddEntry("connections.json", bytes);
            }
        }

        [CompilerGenerated]
        private sealed class <DeserializeExtraInfo>c__AnonStoreyBA
        {
            internal int totCount;

            internal bool <>m__12(GraphNode node)
            {
                this.totCount = Math.Max(node.NodeIndex, this.totCount);
                if (node.NodeIndex == -1)
                {
                    Debug.LogError("Graph contains destroyed nodes. This is a bug.");
                }
                return true;
            }
        }

        [CompilerGenerated]
        private sealed class <DeserializeExtraInfo>c__AnonStoreyBB
        {
            internal GraphNode[] int2Node;
            internal BinaryReader reader;

            internal bool <>m__13(GraphNode node)
            {
                this.int2Node[this.reader.ReadInt32()] = node;
                return true;
            }
        }

        [CompilerGenerated]
        private sealed class <DeserializeExtraInfo>c__AnonStoreyBC
        {
            internal GraphSerializationContext ctx;

            internal bool <>m__14(GraphNode node)
            {
                node.DeserializeReferences(this.ctx);
                return true;
            }
        }

        [CompilerGenerated]
        private sealed class <SerializeExtraInfo>c__AnonStoreyB7
        {
            internal int totCount;

            internal bool <>m__F(GraphNode node)
            {
                this.totCount = Math.Max(node.NodeIndex, this.totCount);
                if (node.NodeIndex == -1)
                {
                    Debug.LogError("Graph contains destroyed nodes. This is a bug.");
                }
                return true;
            }
        }

        [CompilerGenerated]
        private sealed class <SerializeExtraInfo>c__AnonStoreyB8
        {
            internal int c;
            internal BinaryWriter wr;

            internal bool <>m__10(GraphNode node)
            {
                this.c = Math.Max(node.NodeIndex, this.c);
                this.wr.Write(node.NodeIndex);
                return true;
            }
        }

        [CompilerGenerated]
        private sealed class <SerializeExtraInfo>c__AnonStoreyB9
        {
            internal GraphSerializationContext ctx;

            internal bool <>m__11(GraphNode node)
            {
                node.SerializeReferences(this.ctx);
                return true;
            }
        }
    }
}

